<?php
declare(strict_types=1);

namespace App\Chat\Service;


use App\Chat\Cache\LastMessage;
use App\Chat\Cache\UnreadTalkCache;
use App\Chat\Constant\TalkEventConstant;
use App\Chat\Constant\TalkMessageType;
use App\Chat\Constant\TalkModeConstant;
use App\Chat\Event\TalkEvent;
use App\Common\Constants\GenderConstants;
use App\Common\Robot\Robot;
use App\CustHub\Constants\MsgTypeConstants;
use App\CustHub\Mapper\ChContactMapper;
use App\CustHub\Mapper\ChCustomerServiceMapper;
use App\CustHub\Mapper\ChRobotMapper;
use App\CustHub\Mapper\ChRoomMapper;
use App\CustHub\Model\ChChatRecord;
use App\CustHub\Model\ChContact;
use App\CustHub\Model\ChCustomerService;
use App\CustHub\Model\ChRobot;
use App\CustHub\Model\ChTalkRecord;
use App\CustHub\Model\ChTalkRoomRecord;
use App\CustHub\Model\ChTalkSession;
use App\Robot\Dto\RobotMsgDto;
use App\Robot\Service\RobotMsgService;
use Carbon\Carbon;
use Exception;
use Hyperf\DbConnection\Db;
use Hyperf\Di\Annotation\Inject;
use Mine\Exception\NormalStatusException;
use function Hyperf\Collection\collect;

class TalkMessageService
{

    #[Inject]
    protected ChRobotMapper $robotMapper;

    #[Inject]
    protected ChContactMapper $chContactMapper;

    #[Inject]
    protected ChRoomMapper $roomMapper;

    #[Inject]
    protected TalkSessionService $sessionService;

    #[Inject]
    protected ChCustomerServiceMapper $customerServiceMapper;

    public function send(array $data)
    {
        $robotInfo = $this->robotMapper->findById($data['robot_id']);
        if(empty($robotInfo) || $robotInfo->login_status != 1){
            throw new Exception('企微号离线，发送失败~');
        }

        if($data['talk_type'] == 1){
            $contactInfo = $this->chContactMapper->findByRobotIdAndUserId($robotInfo->id,$data['receiver_id']);
            if(empty($contactInfo)){
                throw new Exception('用户不存在~');
            }
            $toId = $contactInfo->user_id;
        }else{
            $roomInfo = $this->roomMapper->findByRoomWxid($data['receiver_id'],$robotInfo->main_uid);
            if(empty($roomInfo)){
                throw new Exception('群不存在~');
            }
            $toId = $roomInfo->room_wxid;
        }


        $msgContent = json_decode($data['msg_content'],true);

        foreach ($msgContent as $item){

            if(!empty($item['record_id'])){
                $this->sendQuoteMsg([
                    'uid' => $data['uid'],
                    'main_uid' => $data['main_uid'],
                    'record_id' => $item['record_id'],
                    'msg_content' => $item
                ]);
                continue;
            }

            $message = $data;
            $msgType = $item['msg_type'];
            $message['user_id'] = $data['uid'];
            $message['msg_type'] = $msgType;
            $message['sender'] = $robotInfo->user_id;
            if($message['talk_type'] == 2){
                $message['from_room_id'] = $data['receiver_id'];
                $message['receiver'] = 0;
            }else{
                $message['receiver'] = $data['receiver_id'];
            }

            $fileName = $item['file_name'] ?? '';

            $msgData = new RobotMsgDto();
            $msgData->setMsgType($item['msg_type'])
                ->setContent($item['content'])
                ->setFileName($fileName)
                ->setToId($toId)
                ->setServerId($robotInfo->server_id)
                ->setUuid($robotInfo->uuid);

            //消息发送服务
            $rst = make(\App\Robot\Service\RobotMessageService::class, [$msgData])->sendMessage();
            if($rst['status'] === false){
                throw new Exception('发送失败~');
            }

            $msgId = $rst['msg_server_id'];
            $msg_unique_identifier = $rst['msg_unique_identifier'] ?? '';
            $message['msg_unique_identifier'] = $msg_unique_identifier;

            //缓存消息发送状态
            redis()->set(ChTalkRecord::CUSTOMER_SERVICE_SENT.$msgId,1,5 * 60);
            $message['msg_id'] = $msgId;

            if($msgType == MsgTypeConstants::TEXT){
                $content = $text = htmlspecialchars($item['content']);
            }else{
                $text = MsgTypeConstants::getMessage($msgType);
                $content = $item['content'];
            }

            $message['content'] = [
                'content' => $content,
                'msg_type' => $msgType
            ];

            if(!empty($fileName)){
                $message['content']['file_name'] = $fileName;
            }

            if($msgType == MsgTypeConstants::TEXT){
                $message['text'] = $text;
            }
            $message['is_cs'] = 1;//客服发送
            $this->insert($message, $text);
        }
    }

    public function sendQuoteMsg(array $data)
    {

        $recordInfo = ChTalkRecord::query()
            ->where('id',$data['record_id'])
            ->first();

        if(empty($recordInfo)){
            throw new NormalStatusException('引用的消息不存在~');
        }

        $robotInfo = $this->robotMapper->findById($recordInfo->robot_id);
        if(empty($robotInfo) || $robotInfo->login_status != 1){
            throw new NormalStatusException('企微号离线，发送失败~');
        }

        $isRobot = false;
        if($recordInfo->is_cs){
            $isRobot = true;
            $userId = $robotInfo->user_id;
            $toId = $recordInfo->talk_type == TalkModeConstant::PRIVATE_CHAT ? $recordInfo->receiver : $recordInfo->from_room_id;
        }else{
            $toId = $recordInfo->talk_type == TalkModeConstant::PRIVATE_CHAT ? $recordInfo->sender : $recordInfo->from_room_id;
            $userId = $recordInfo->sender;
        }
        $fileType= 0;
        switch ($recordInfo->msg_type){
            case MsgTypeConstants::TEXT:
                $quoteMsgType = 0;
                break;
            case MsgTypeConstants::IMAGE:
                $fileType = 1;
                $quoteMsgType = 14;
                break;
            case MsgTypeConstants::FILE:
                $fileType = 5;
                $quoteMsgType = 15;
                break;
            case MsgTypeConstants::VIDEO:
                $fileType = 4;
                $quoteMsgType = 23;
                break;
            case MsgTypeConstants::EMOJI:
                $quoteMsgType = 29;
                break;
            default:
                throw new NormalStatusException('当前消息不支持引用~');
        }

        $reply = [
            'type' => $fileType,
            'msgServerId' => $recordInfo->msg_id,
            'userId' => $userId,
            'showName' => '',
            'timeStamp' => empty($recordInfo->send_time) ? time() : $recordInfo->send_time,
            'msgUniqueIdentifier' => $recordInfo->msg_unique_identifier,
        ];



        if($quoteMsgType == 0){
            $msgData = [
                'content' => $recordInfo->text,
            ];
        } elseif (!$isRobot && in_array($recordInfo->msg_type, [MsgTypeConstants::IMAGE, MsgTypeConstants::FILE, MsgTypeConstants::VIDEO])) {
//            //需要下载
//            $filename = '';
//            if(!empty($recordInfo->content['content'])){
//                $url = $recordInfo->content['content'];
//                $filename = $recordInfo->content['file_name'] ?? '';
//            }else{
//                $url = container()->get(RobotMsgService::class)->processMessages($robotInfo,$recordInfo->msg_type,$recordInfo->msg_data);
//                if($url === false){
//                    throw new NormalStatusException('引用消息资源下载失败，请重试~');
//                }
//            }
//            $apiService = make(Robot::class,[$robotInfo->server_id,$robotInfo->uuid])->gateway();
//            $response = $apiService->cdnBigUpload($url,$filename,$fileType);
//            if($response === false){
//                throw new NormalStatusException('引用消息资源上传失败，请重试~');
//            }
//            $msgData = underToCamel($response);
            //测试不需要重新上传也可以，在测试下看看
            $recordMsgData = $recordInfo->msg_data;
            $recordMsgData['type'] = $recordMsgData['msgtype'] ?? 0;
            $msgData = underToCamel($recordMsgData['msg_data']);
        } else {
            $recordMsgData = $recordInfo->msg_data;
            $msgData = underToCamel($recordMsgData['msg_data']);
        }

        if(is_array($data['msg_content'])){
            $msgContent = $data['msg_content'];
        }else{
            $msgContent = json_decode($data['msg_content'],true);
        }

        $msgType = $msgContent['msg_type'];
        $content = $msgContent['content'];

        $reply['msgData'] = $msgData;

        $msgData = new RobotMsgDto();
        $msgData->setMsgType($msgType)
            ->setContent($content)
            ->setReply($reply)
            ->setToId($toId)
            ->setServerId($robotInfo->server_id)
            ->setUuid($robotInfo->uuid);
        $rst = make(\App\Robot\Service\RobotMessageService::class, [$msgData])->sendMessage();

        if($rst['status'] === false){
            throw new NormalStatusException('发送失败~');
        }

        $message['main_uid'] = $data['main_uid'];
        $message['user_id'] = $data['uid'];
        $message['msg_type'] = $msgType;
        $message['robot_id'] = $recordInfo->robot_id;
        $message['sender'] = $robotInfo->user_id;
        $message['talk_type'] = $recordInfo->talk_type;
        if($message['talk_type'] == 2){
            $message['from_room_id'] = $toId;
            $message['receiver'] = 0;
        }else{
            $message['receiver'] = $toId;
        }

        $msgId = $rst['msg_server_id'];
        $msg_unique_identifier = $rst['msg_unique_identifier'] ?? '';
        $message['msg_unique_identifier'] = $msg_unique_identifier;

        //缓存消息发送状态
        redis()->set(ChTalkRecord::CUSTOMER_SERVICE_SENT.$msgId,1,5 * 60);
        $message['msg_id'] = $msgId;

        $message['msg_data'] = [
            'msgtype' => 2,
            'msg_data' => $content
        ];

        if($msgType == MsgTypeConstants::TEXT){
            $content = $text = htmlspecialchars($content);
        }else{
            $text = MsgTypeConstants::getMessage($msgType);
        }

        $message['content'] = [
            'content' => $content,
            'msg_type' => $msgType
        ];

        if($msgType == MsgTypeConstants::TEXT){
            $message['text'] = $text;
        }
        $message['is_cs'] = 1;//客服发送
        $message['quote_id'] = $recordInfo->id;
        $this->insert($message, $text);
    }

    public function recordHandle(ChRobot $robot,array $data): void
    {
        $msg_unique_identifier = str_replace('_need_verify', '', $data['msg_unique_identifier']);

        $record = ChTalkRecord::query()
            ->where('robot_id',$robot->id)
            ->where('msg_unique_identifier',$msg_unique_identifier)
            ->first();
        if(!empty($record)){
            $record->is_need_verify = 1;
            $record->need_verify_msg = str_replace(' 发送联系人验证', '', $data['content']);
            $record->save();

            event(new TalkEvent(TalkEventConstant::EVENT_TALK_NEED_VERIFY, [
                'record_id' => $record->id
            ]));
        }
    }

    /**
     * 客户消息插入
     * @param array $message
     * @param string $text
     * @return bool
     */
    public function insert(array $message,string $text,bool $isFirst = false,$sessionId = ''): bool
    {
        $result = ChTalkRecord::create($message);

        $this->handle($result, ['text' => mb_substr($text, 0, 30),'sessionId' => $sessionId],$isFirst);

        return true;
    }

    /**
     * 创建文本消息
     *
     * @param array $message
     * @return bool
     */
    public function insertText(array $message): bool
    {
        $message['msg_type'] = TalkMessageType::TEXT_MESSAGE;
        $message['content']  = [
            'content' =>  htmlspecialchars($message['content']),
            'msg_type' => $message['type']
        ];

        $message['text'] =$message['content']['content'];

        $result = ChTalkRecord::create($message);

        $this->handle($result, ['text' => mb_substr($result->content, 0, 30)]);
        return true;
    }

    /**
     * 处理数据
     *
     * @param ChTalkRecord $record
     * @param array       $option
     */
    private function handle(ChTalkRecord $record, array $option = [],bool $isFirst = false): void
    {
        if($record->user_id == 0){
            return;
        }
        if($record->talk_type == TalkModeConstant::PRIVATE_CHAT){
            $receiver = $record->is_cs ? $record->receiver : $record->sender;
        }else{
            $receiver = $record->from_room_id;
        }
        stdout_log()->info("消息缓存".($record->is_cs ? '客服发送' : '用户发送'));
        if ($record->is_cs == 0) {
            //记录未读消息数量
            stdout_log()->info('缓存未读消息数量');
            UnreadTalkCache::getInstance()->increment($record->user_id,$record->robot_id,$record->talk_type,$receiver);
            try {
                if(!empty($option['sessionId'])){
                    $this->sessionService->increment($option['sessionId']);
                }
            }catch (\Throwable $e){
                stdout_log()->info('缓存未读消息数量失败');
            }
        }

        stdout_log()->info("最后一条消息缓存".json_encode([
            'robot_id' => $record->robot_id,
            'talk_type' => $record->talk_type,
            'receiver' => $receiver,
            'text' => $option['text'],
            'created_at' => date('Y-m-d H:i:s')
        ]));

        LastMessage::getInstance()->save($record->talk_type, $record->robot_id, $receiver, [
            'text'       => $option['text'],
            'created_at' => date('Y-m-d H:i:s')
        ]);

        $data = [
            'user_id'     => $record->user_id,
            'form_room_id' => $record->from_room_id,
            'sender_id'   => $record->sender,
            'receiver_id' => $record->receiver,
            'talk_type'   => $record->talk_type,
            'record_id'   => $record->id,
        ];

        if($record->talk_type ==2 ){
            $roomInfo = $this->roomMapper->findByRoomWxid($record->from_room_id,$record->main_uid);
            $name = $roomInfo->room_name;
            $avatarUrl = $roomInfo->room_avatar_url;
            $room_is_forbid_change_name = $roomInfo->room_is_forbid_change_name;
        }else{
            $contactInfo = $this->chContactMapper->findByRobotIdAndUserId($record->robot_id,$receiver);
            $name = $contactInfo->nickname;
            $avatarUrl = $contactInfo->avatar;
            $room_is_forbid_change_name = 0;
        }


        $talkSession = ChTalkSession::query()
            ->where('robot_id',$record->robot_id)
            ->where('user_id',$record->user_id)
            ->where('receiver_id',$receiver)
            ->first();

        if(empty($talkSession)){
            return;
        }

        if($record->is_cs){
            $updateData = [
                'cs_num' => $talkSession->cs_num + 1
            ];
        }else{
            if($talkSession->contact_num==0){
                //第一次回复记录回复时间
                $updateData['contact_first_reply']=time() - $record->created_at->getTimestamp();
            }
            $updateData = [
                'contact_num' => $talkSession->contact_num + 1
            ];
        }

        ChTalkSession::query()->where('id',$talkSession->id)->update($updateData);

        $roomData = ChTalkSession::item([
            'id'          => $talkSession['id'],
            'robot_id'    => $talkSession['robot_id'],
            'talk_type'   => $talkSession['talk_type'],
            'receiver_id' => $talkSession['receiver_id'],
            'is_top'      => $talkSession['is_top'],
            'is_disturb'  => $talkSession['is_disturb'],
            'is_hidden'   => $talkSession['is_hidden'],
            'is_pending'  => $talkSession['is_pending'],
            'is_blocked'  => $talkSession['is_blocked'],
            'is_robot'    => $talkSession['is_robot'],
            'updated_at'  => Carbon::parse($talkSession['updated_at'])->toDateTimeString(),
        ]);

        $roomData['name']   = $name;
        $roomData['avatar'] = $avatarUrl;
        $roomData['room_is_forbid_change_name'] = $room_is_forbid_change_name;

        //未读消息数
        $roomData['unread_num']  = UnreadTalkCache::getInstance()->read($talkSession->user_id,$talkSession->robot_id, $talkSession->talk_type, $talkSession->receiver_id);

        $records = LastMessage::getInstance()->read($talkSession->talk_type, $talkSession->robot_id, $talkSession->receiver_id);
        if ($records) {
            $roomData['msg_text']   = $records['text'];
            $roomData['updated_at'] = $records['created_at'];
        }
        $roomData['gender_text'] = '';
        $data['room_data'] = $roomData;
        //引用消息
        $data['quote_id'] = $record->quote_id;
        stdout_log()->info('引用消息ID'.$record->quote_id);
        if(!empty($record->quote_id)){
            $quoteMap = container()->get(TalkService::class)
                ->getQuoteContent([$record->quote_id],$record->talk_type);
            $data['quote_content'] = $quoteMap[$record->quote_id] ?? [];
        }else{
            $data['quote_content'] = [];
        }

        event(new TalkEvent(TalkEventConstant::EVENT_TALK, $data));

        if(!$record->is_cs && $record->talk_type == TalkModeConstant::PRIVATE_CHAT){
            $csId = $record->user_id;
            $customerService = $this->customerServiceMapper->findById($csId);
            if(!empty($customerService) && $customerService->status == 1 && $customerService->online_status == 0){
                //检测当前时间是否在工作时段内，不存在则发送默认回复消息
                $now = Carbon::now();
                $workTime = collect($customerService->work_time);
                //每天
                $notInWorkTime = $this->checkWorkTime($workTime,0, $now);
                $isSend = false;
                if($notInWorkTime){
                    $isSend = true;
                }elseif($now->isWeekend()){
                    //周末工作时间段
                    $isSend = $this->checkWorkTime($workTime,1, $now);
                }

                if($isSend){
                    $autoReply = $customerService->auto_reply;
                    if($autoReply){
                        $sendData = [
                            'robot_id' => $record->robot_id,
                            'main_uid' => $record->main_uid,
                            'uid' => $record->user_id,
                            'talk_type' => TalkModeConstant::PRIVATE_CHAT,
                            'receiver_id' => $record->sender,
                            'msg_content' => json_encode([
                                [
                                    'msg_type' => MsgTypeConstants::TEXT,
                                    'content' => $autoReply
                                ]
                            ])
                        ];
                        try {
                            $this->send($sendData);
                        }catch (\Throwable $e){
                            stdout_log()->error("自动回复发送失败".$e->getMessage());
                        }
                    }
                }
            }
        }
    }

    private function checkWorkTime($workTime,$type,$now): bool
    {
        return $workTime->filter(function ($item) use ($type,$now){
            return isset($item['type']) && $item['type'] == $type && (Carbon::parse($item['start_time'])->gt($now) || Carbon::parse($item['end_time'])->lt($now));
        })->count() == $workTime->count();
    }


    public function recallMessage(array $params): bool
    {
        $info = ChTalkRecord::query()
            ->where('id',$params['id'])
            ->first();
        if(!$info || $info->is_revoke == 1){
            return false;
        }

        if($info->created_at->addMinutes(2)->lt(Carbon::now())){
            return false;
        }

        $robotInfo = $this->robotMapper->findById($info->robot_id);

        $service = make(Robot::class,[$robotInfo->server_id,$robotInfo->uuid])->gateway();
        $chatId = $info->talk_type == TalkModeConstant::PRIVATE_CHAT ? $info->receiver : $info->from_room_id;
        $rst = $service->revokeMsg($chatId,$info->msg_id);

        if($rst !== false){
            ChTalkRecord::query()
                ->where('id',$params['id'])
                ->update([
                    'is_revoke' => 1
                ]);
            return true;
        }

        return false;
    }
}
